#pragma once


#include "Utility/Algebra/Matrix.h"
#include "Utility/Algebra/Point.h"
#include "DataStructure/Common/Status.h"
#include "DataStructure/Common/Id.h"
#include "DataStructure/Input/Coordinate/ICoordinate.h"
#include "DataStructure/Input/Node/NodeData.h"
#include "DBManager/DBServiceFactory.h"

#include "SGFem/Pre/Common/PreExport.h"
#ifdef TEST
#include "gtest/gtest_prod.h"
#endif

SG_NAMESPACE_OPEN
namespace FEMSolver
{
/** 
 * @brief 坐标系处理类, 实现为单例类。保存所有局部坐标系的原点和基矢量
 */
class Pre_DECLSPEC CoordinateProcessor
{
private:
   	CoordinateProcessor() = default;
    ~CoordinateProcessor() = default;
    CoordinateProcessor(const CoordinateProcessor&) = delete;
    const CoordinateProcessor& operator=(const CoordinateProcessor&) = delete;

    /** @brief 坐标系初始化， 通过数据库判断具备的坐标系类型
     * 
     * 每个坐标系都使用相应的数据进行初始化
    */
    static void ReadCoord (const SG::DBManager::DBServiceFactorySP dbServiceFactory, SG::DataStructure::FEM::ICoordinatePArray& _OUT coordSet);
    
    /** @brief 计算各个坐标系的原点和基矢量*/
    static void Compute (const SG::DBManager::DBServiceFactorySP dbServiceFactory, SG::DataStructure::FEM::ICoordinatePArray& _OUT coordSet);

#ifdef TEST
        FRIEND_TEST (CoordinateProcessorTest, Coordinate_dependent_Test);
#endif
public:
    /// @brief 获取唯一实例
    static CoordinateProcessor& GetInstance();

    /** @brief 初始化，计算所有局部坐标系的基矢量和全局坐标系
     * @param [in] dbServiceFactory   网格模型数据
    */
    void Initialize (const SG::DBManager::DBServiceFactorySP dbServiceFactory);

    /// @brief 清空存储的所有坐标系
    void Clear();
    
    /** @brief 将计算好原点和基矢量的坐标系保存到本单例类的成员变量中*/
    void SetCoordSet (const SG::DataStructure::FEM::ICoordinatePArray& coordSet);

    /** @brief 通过坐标系编号，返回坐标系
     * 
     * 对应坐标系数据不存在，将返回空指针。
    */
    const SG::DataStructure::FEM::ICoordinateP GetCoord (SG::DataStructure::Common::Id id) const;

    /** @brief 计算指定坐标系在空间某点处的3个基矢量[ 柱坐标和球坐标，在不同空间点处的基矢量方向不同]
     * 
     * @param [in] cId 坐标系编号
     * @param [in] point 空间点的Basic坐标
     * @param [out] 空间点处基矢量， 
     */
    void GetCoordTrans (const SG::DataStructure::Common::Id cId, const SG::Algebra::Point& point, _OUT SG::Algebra::Matrix& T) const;

    /** @brief 将点转换到世界坐标系下
     * 
     * @param cId    局部坐标系编号，坐标系编号必须大于0
     * @param point  节点在局部坐标系下坐标值
     * @param pNew   节点在世界坐标下分量
     */
    void Translate2Basic (const SG::DataStructure::Common::Id cId, const SG::Algebra::Point& point, SG::Algebra::Point& _OUT pNew) const;

    /** @brief 计算向量在全局坐标下分量（不用进行偏移）
     * 
     */
    void Rotate2Basic (const SG::DataStructure::Common::Id cId, _OUT SG::Algebra::Vector3D& vec) const;

    // ToDo.【wanghuan】 去掉形参 dispCoordId， 其数值直接从 node 的成员变量获得
    /** @brief 计算节点在全局坐标系下分量，并将位移输出坐标系id返回
     * 
     * @param[in] node 节点定义
     * @param[out] point 节点在世界坐标系下坐标值
     * @param[out] dispCoordId 节点处定义的位移输出坐标系
     */
    void GetGlobalCoordinate (const SG::DataStructure::FEM::NodeData node, Algebra::Point& point, SG::DataStructure::Common::Id& dispCoordId) const;
private:
    SG::DataStructure::FEM::ICoordinatePArray m_coordSet;      ///< 模型中的局部坐标系  
};

inline void CoordinateProcessor::Clear()
{
    m_coordSet.clear ();
}
};
SG_NAMESPACE_CLOSE